Shellsort, also known as Shell sort or Shell's method, is an in-place comparison sort. It can be understood as either a generalization of sorting by exchange (bubble sort) or sorting by insertion (insertion sort). The method starts by sorting pairs of elements far apart from each other, then progressively reducing the gap between elements to be compared. By starting with far-apart elements, it can move some out-of-place elements into the position faster than a simple nearest-neighbor exchange. The running time of Shellsort is heavily dependent on the gap sequence it uses. For many practical variants, determining their time complexity remains an open problem.
The algorithm was first published by Donald Shell in 1959, and has nothing to do with shells.Some older textbooks and references call this the "Shell–Metzner" sort after Marlene Metzner Norton, but according to Metzner, "I had nothing to do with the sort, and my name should never have been attached to it." See
Beginning with large values of ''h'' allows elements to move long distances in the original list, reducing large amounts of disorder quickly, and leaving less work for smaller ''h''-sort steps to do.
In simplistic terms, this means if we have an array of 1024 numbers, our first gap ( h) could be 512. We then run through the list comparing each element in the first half to the element in the second half. Our second gap ( k) is 256, which breaks the array into four sections (starting at 0, 256, 512, 768), and we make sure the first items in each section are sorted relative to each other, then the second item in each section, and so on. In practice the gap sequence could be anything, but the last gap is always 1 to finish the sort (effectively finishing with an ordinary insertion sort).
An example run of Shellsort with gaps 5, 3 and 1 is shown below.
The first pass, 5-sorting, performs insertion sort on five separate subarrays ( a1, a6, a11), ( a2, a7, a12), ( a3, a8), ( a4, a9), ( a5, a10). For instance, it changes the subarray ( a1, a6, a11) from (62, 17, 25) to (17, 25, 62). The next pass, 3-sorting, performs insertion sort on the three subarrays ( a1, a4, a7, a10), ( a2, a5, a8, a11), ( a3, a6, a9, a12). The last pass, 1-sorting, is an ordinary insertion sort of the entire array ( a1,..., a12).
As the example illustrates, the subarrays that Shellsort operates on are initially short; later they are longer but almost ordered. In both cases insertion sort works efficiently.
Unlike insertion sort, Shellsort is not a stable sort since gapped insertions transport equal elements past one another and thus lose their original order. It is an Adaptive sort in that it executes faster when the input is partially sorted.
// Sort an array a0...n-1.
List
// Start with the largest gap and work down to a gap of 1
// similar to insertion sort but instead of 1, gap is being used in each step
foreach (int gap in gaps)
{
// Do a gapped insertion sort for every element in gaps
// Each loop leaves a[0..gap-1] in gapped order
for (int i = gap; i < n; ++i)
{
// save a[i] in temp and make a hole at position i
int temp = a[i];
// shift earlier gap-sorted elements up until the correct location for a[i] is found
for (int j = i; (j >= gap) && (a[j - gap] > temp); j -= gap)
{
a[j] = a[j - gap];
}
// put temp (the original a[i]) in its correct location
a[j] = temp;
}
}
The table below compares most proposed gap sequences published so far. Some of them have decreasing elements that depend on the size of the sorted array ( N). Others are increasing infinite sequences, whose elements less than N should be used in reverse order.
| e.g. | Donald Shell, 1959 | |||
| Frank & Lazarus, 1960 | ||||
| Hibbard, 1963 | ||||
| , prefixed with 1 | Papernov & Stasevich, 1965 | |||
| Successive numbers of the form (3-smooth numbers) | Pratt, 1971 | |||
| , not greater than | Donald Knuth, 1973, based on Pratt, 1971 | |||
| Incerpi & Sedgewick, 1985, Donald Knuth | ||||
| , prefixed with 1 | Sedgewick, 1982 | |||
| Sedgewick, 1986
| ||||
| for | Gaston Gonnet & , 1991 (1991). 9780201416077, Addison-Wesley. ISBN 9780201416077 | |||
| (or equivalently, ) | Tokuda, 1992 (misquote per OEIS) | |||
| Approximately | Ciura, 2001 | |||
| Lee, 2021 | ||||
| Skean, Ehrenborg, Jaromczyk, 2023 |
The Gonnet and Baeza-Yates implementation of Shellsort (GBY91) also uses gaps that start with N and performs better than many of the other sequences on average, but is also prone to a Θ( N2) worst case: Let X be a gap in the sequence. The GBY91 gaps' common ratio of 2.2 permits at least two consecutive integers Y and Y+1 to equal X from floor division by 2.2. Choose Y or Y+1, whichever one is even, to repeat the process, and one obtains an infinitely long chain of even gaps > X by induction. With X = 1, one generates infinite options for N where all GBY91 gaps > 1 are even and thus are Θ( N2) on the same aforementioned worst case input for Shell's original gaps.
Although it has higher complexity than the O( N log N) that is optimal for comparison sorts, Pratt's version lends itself to and has the same asymptotic gate complexity as Batcher's bitonic sorter.
Gonnet and Baeza-Yates observed that Shellsort makes the fewest comparisons on average when the ratios of successive gaps are roughly equal to 2.2. This is why their sequence with ratio 2.2 and Tokuda's sequence with ratio 2.25 prove efficient. However, it is not known why this is so. Sedgewick recommends using gaps which have low greatest common divisors or are pairwise coprime. Using gaps which are odd numbers appears to help - speedups of about 25% have been seen in practice versus Shell's original code. Using gaps which are not multiples of 2, 3 or 5 appear to reduce run times further - speedups of about 35% versus Shell's original seem possible. Using gaps which are prime numbers only (ending with 1), appear to produce speedups of about 40% over the original code.
With respect to the average number of comparisons, Ciura's sequence has the best known performance; gaps greater than 701 were not determined but the sequence can be further extended according to the recursive formula .
Tokuda's sequence, defined by the simple formula , where , , can be recommended for practical applications.
If the maximum input size is small, as may occur if Shellsort is used on small subarrays by another recursive sorting algorithm such as quicksort or merge sort, then it is possible to tabulate an optimal sequence for each input size. Additional commentary at Fastest gap sequence for shell sort? (23 May 2018). For N = 128 and N = 1000, Ciura empirically found that (1, 4, 9, 24, 85) and (1, 4, 10, 23, 57, 156, 409, 995) made the fewest number of comparisons on average respectively.
Mark Allen Weiss proved that Shellsort runs in O( N log N) time when the input array is in reverse order.
With respect to the average number of operations, none of the proven results concerns a practical gap sequence. For gaps that are powers of two, Espelid computed this average as . The quoted result is equation (8) on p. 399. Donald Knuth determined the average complexity of sorting an N-element array with two gaps ( h, 1) to be . It follows that a two-pass Shellsort with h = Θ( N1/3) makes on average O( N5/3) comparisons/inversions/running time. Andrew Yao found the average complexity of a three-pass Shellsort. His result was refined by Svante Janson and Knuth: the average number of comparisons/inversions/running time made during a Shellsort with three gaps ( ch, cg, 1), where h and g are coprime, is in the first pass, in the second pass and in the third pass. ψ( h, g) in the last formula is a complicated function asymptotically equal to . In particular, when h = Θ( N7/15) and g = Θ( N1/5), the average time of sorting is O( N23/15).
Based on experiments, it is conjectured that Shellsort with Hibbard's gap sequence runs in O( N5/4) average time, and that Gonnet and Baeza-Yates's sequence requires on average 0.41 N ln N (ln ln N + 1/6) element moves. Approximations of the average number of operations formerly put forward for other sequences fail when sorted arrays contain millions of elements.
The graph below shows the average number of element comparisons use by various gap sequences, divided by the theoretical lower bound, i.e. log2 N!. Ciuria's sequence 1, 4, 10, 23, 57, 132, 301, 701 (labelled Ci01) has been extended according to the formula .
Applying the theory of Kolmogorov complexity, Jiang, Ming Li, and Vitányi proved the following lower bound for the order of the average number of operations/running time in a p-pass Shellsort: Ω( pN1+1/ p) when p ≤ log2 N and Ω( pN) when p > log2 N. Therefore, Shellsort has prospects of running in an average time that asymptotically grows like N log N only when using gap sequences whose number of gaps grows in proportion to the logarithm of the array size. It is, however, unknown whether Shellsort can reach this asymptotic order of average-case complexity, which is optimal for comparison sorts. The lower bound was improved by Vitányi for every number of passes to where . This result implies for example the Jiang-Li-Vitányi lower bound for all -pass increment sequences and improves that lower bound for particular increment sequences. In fact all bounds (lower and upper) currently known for the average case are precisely matched by this lower bound. For example, this gives the new result that the Janson-Knuth upper bound is matched by the resulting lower bound for the used increment sequence, showing that three pass Shellsort for this increment sequence uses comparisons/inversions/running time. The formula allows us to search for increment sequences that yield lower bounds which are unknown; for example an increment sequence for four passes which has a lower bound greater than for the increment sequence . The lower bound becomes
The worst-case complexity of any version of Shellsort is of higher order: Plaxton, Bjorn Poonen, and Torsten Suel showed that it grows at least as rapidly as .
Shellsort can also serve as a sub-algorithm of introsort, to sort short subarrays and to prevent a slowdown when the recursion depth exceeds a given limit. This principle is employed, for instance, in the bzip2 compressor.
|
|